package me.sdevil507.supports.shiro.filter;

import lombok.extern.slf4j.Slf4j;
import me.sdevil507.supports.helper.JsonResponseHelper;
import me.sdevil507.supports.result.ApiResultDTO;
import me.sdevil507.supports.result.ApiResultGenerator;
import me.sdevil507.supports.shiro.enums.LoginChannel;
import me.sdevil507.supports.shiro.enums.LoginModeType;
import me.sdevil507.supports.shiro.token.JWTToken;
import me.sdevil507.supports.status.ApiStatusCode;
import me.sdevil507.supports.util.JwtUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {
    /**
     * 如果带有 token，则对 token 进行检查，否则直接通过
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        //判断请求的请求头是否带上 "Token"
        if (isLoginAttempt(request, response)) {
            //如果存在，则进入 executeLogin 方法执行登入，检查 token 是否正确
            try {
                return executeLogin(request, response);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }else{
            return false;
        }
    }

    /**
     * 判断用户是否想要登入。
     * 检测 header 里面是否包含 Token 字段
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String token = req.getHeader(JwtUtil.JWT_SHIRO_CACHE_KEY_PREFIX);
        return token != null;
    }

    /**
     * 执行登陆操作
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(JwtUtil.JWT_SHIRO_CACHE_KEY_PREFIX);
        JWTToken jwtToken = new JWTToken(JwtUtil.getPhoneNumber(token), LoginChannel.WXAPPLET, LoginModeType.WECHAT);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获 是调用doGetAuthenticationInfo方法
        try {
            getSubject(request, response).login(jwtToken);
        }catch (AuthenticationException ex){
            ex.printStackTrace();
            //TODO: 认证异常，通常是token在redis里面失效
            return false;
        }
        // 如果没有抛出异常则代表登入成功，返回true
        return true;
    }

    /**
     * 表示访问拒绝时是否自己处理，如果返回true表示自己不处理且继续拦截器链执行，返回false表示自己已经处理了（比如重定向到另一个页面）
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);

        ApiResultDTO apiResultDTO= ApiResultGenerator.create(ApiStatusCode.ACCOUNT_TOKEN_INVALID.getCode(), ApiStatusCode.ACCOUNT_TOKEN_INVALID.getDescription());

        JsonResponseHelper.exec(httpServletResponse, HttpServletResponse.SC_UNAUTHORIZED, apiResultDTO);

        return false;
    }

}
